package cn.edu.neu.vmmigeval.cloud.policy.impl;


import cn.edu.neu.vmmigeval.cloud.entity.Host;
import cn.edu.neu.vmmigeval.cloud.entity.Vm;
import cn.edu.neu.vmmigeval.cloud.policy.MigratedHostPolicy;
import cn.edu.neu.vmmigeval.cloud.util.MathUtil;
import cn.edu.neu.vmmigeval.cloud.util.UtilizationHistoryUtil;

public class MigratedHostPolicyLocalRegression extends MigratedHostPolicy {

	/** The scheduling interval. */
	private double schedulingInterval = 2;

	/** The safety parameter. */
	private double safetyParameter = 1.2;
	
	public MigratedHostPolicyLocalRegression(double safetyParameter,double schedulingInterval){
		super();
		this.schedulingInterval = schedulingInterval;
		this.safetyParameter = safetyParameter;
	}
	public MigratedHostPolicyLocalRegression(){
		
	}
	
	@Override
	public boolean isHostOverUtilized(Host host) {
		//Host _host = host;
		double[] utilizationHistory = UtilizationHistoryUtil.getUtilizationHistory(host.getUtilizationHistory());
		int length = 10; // we use 10 to make the regression responsive enough to latest values
		if (utilizationHistory.length < length) {
			//return getFallbackVmAllocationPolicy().isHostOverUtilized(host);
			
			double cpuUtilization=0.0;
			if(host.getActive()==0&&!host.isMigration()){
				
				cpuUtilization = (double)host.getCurrentCpu()/host.getTotalCpu();
				if(cpuUtilization>=0.9){
					return true;
				}
			}
			
		}
		double[] utilizationHistoryReversed = new double[length];
		for (int i = 0; i < length; i++) {
			utilizationHistoryReversed[i] = utilizationHistory[length - i - 1];
		}
		double[] estimates = null;
		try {
			estimates = getParameterEstimates(utilizationHistoryReversed);
		} catch (IllegalArgumentException e) {
			//return getFallbackVmAllocationPolicy().isHostOverUtilized(host);
		}
		double migrationIntervals = Math.ceil(getMaximumVmMigrationTime(host) / getSchedulingInterval());
		
		double predictedUtilization = estimates[0] + estimates[1] * (length + migrationIntervals);
		predictedUtilization *= getSafetyParameter();

		
		//addHistoryEntry(host, predictedUtilization);

		double cpuUtilization=0.0;
		if(host.getActive()==0&&!host.isMigration()){
			cpuUtilization = (double)host.getCurrentCpu()/host.getTotalCpu();
		}
		//System.out.println("is :"+predictedUtilization);
		if(predictedUtilization >= 1&&cpuUtilization>=0.9){
			
			return true;
		}
		return false;
	}

	/**
	 * Gets the parameter estimates.
	 * 
	 * @param utilizationHistoryReversed the utilization history reversed
	 * @return the parameter estimates
	 */
	protected double[] getParameterEstimates(double[] utilizationHistoryReversed) {
		return MathUtil.getLoessParameterEstimates(utilizationHistoryReversed);
	}

	/**
	 * Gets the maximum vm migration time.
	 * 
	 * @param host the host
	 * @return the maximum vm migration time
	 */
	protected double getMaximumVmMigrationTime(Host host) {
		int maxRam = Integer.MIN_VALUE;
		for (Vm vm : host.getVmList()) {
			//int ram = vm.getRam();
			int ram = vm.getTotalRam();
			if (ram > maxRam) {
				maxRam = ram;
			}
		}
		//return maxRam / ((double) host.getBw() / (2 * 8000));
		return maxRam / ((double) host.getTotalBw() / (2 * 8000));
	}

	/**
	 * Sets the scheduling interval.
	 * 
	 * @param schedulingInterval the new scheduling interval
	 */
	protected void setSchedulingInterval(double schedulingInterval) {
		this.schedulingInterval = schedulingInterval;
	}

	/**
	 * Gets the scheduling interval.
	 * 
	 * @return the scheduling interval
	 */
	protected double getSchedulingInterval() {
		return schedulingInterval;
	}
	
	@Override
	public boolean isHostUnderUtilized(Host host) {
		// TODO Auto-generated method stub
		return false;
	}
	
	public double getSafetyParameter() {
		return safetyParameter;
	}

	public void setSafetyParameter(double safetyParameter) {
		this.safetyParameter = safetyParameter;
	}

}
